/*
**********************************************************************
*                         头文件包含
**********************************************************************
*/
#include <intrins.h>
#include <string.h>
#include "Int_rc522.h"
#include "Com_Util.h"

/*
**********************************************************************
*                         本地全局变量
**********************************************************************
*/
// 专用SFR
sfr RCAP2LH = 0xCA;
sfr T2LH    = 0xCC;

// RC522 SPI接口定义
sbit MF522_NSS = P1 ^ 5; // 这是SDA
sbit MF522_SCK = P1 ^ 4;
sbit MF522_SI  = P1 ^ 3;
sbit MF522_SO  = P1 ^ 2;
sbit MF522_RST = P1 ^ 0; //

/****************RC522驱动源码**************************************/

/**
 *
 * 功    能：寻卡
 * @brief PcdRequest 函数用于向PCD（Proximity Coupling Device，proximity coupling device）发送请求命令
 *
 * 该函数根据指定的请求码(req_code)向PCD发送一个请求，以获取标签类型信息。它首先配置通信参数，
 * 然后构建包含请求码的命令缓冲区，通过PCD_TRANSCEIVE方式发送命令，并根据返回的状态和长度判断操作是否成功。
 * 如果操作成功且返回长度符合预期，它将解析并返回接收到的标签类型信息；否则，将返回错误状态。
 *
 * @param req_code 请求码，用于指示要执行的具体请求操作
 *              req_code[IN]:寻卡方式
                                0x52 = 寻感应区内所有符合14443A标准的卡
                                0x26 = 寻未进入休眠状态的卡
 * @param pTagType 指向存储标签类型信息的缓冲区的指针
                pTagType[OUT]：卡片类型代码
                    0x4400 = Mifare_UltraLight
                    0x0400 = Mifare_One(S50)
                    0x0200 = Mifare_One(S70)
                    0x0800 = Mifare_Pro(X)
                    0x4403 = Mifare_DESFire
 * @return char 返回操作状态，MI_OK表示成功，MI_ERR表示错误
 */
char PcdRequest(unsigned char req_code, unsigned char *pTagType)
{
    char status;
    unsigned int unLen;
    unsigned char ucComMF522Buf[MAXRLEN];

    // 清除Status2Reg中的0x08位，准备接收新的响应
    ClearBitMask(Status2Reg, 0x08);

    // 配置位帧寄存器，设置为特定的通信模式
    WriteRawRC(BitFramingReg, 0x07);

    // 设置TxControlReg的低两位，启用数据传输功能
    SetBitMask(TxControlReg, 0x03);

    // 构建命令缓冲区，首字节为请求码
    ucComMF522Buf[0] = req_code;

    // 调用PcdComMF522函数发送命令，PCD_TRANSCEIVE表示发送并接收数据
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &unLen);

    // 如果操作成功且返回长度符合预期，则解析接收到的标签类型信息
    if ((status == MI_OK) && (unLen == 0x10))
    {
        *pTagType       = ucComMF522Buf[0]; // 存储标签类型的第一个字节
        *(pTagType + 1) = ucComMF522Buf[1]; // 存储标签类型的第二个字节
    }
    else
    {
        status = MI_ERR;
    } // 操作失败或返回长度不符合预期，设置状态为错误

    return status; // 返回操作状态
}

/**
 * @brief PCD执行防碰撞操作
 *
 * 本函数执行防碰撞操作以识别唯一卡号(SNR)。防碰撞过程是通过与PICC( proximity coupling device)
 * 交换数据来完成的，目的是从多个卡中选出一个唯一卡进行通信。
 *
 * @param pSnr 卡的序列号，必须足够大以存储完整的SNR  pSnr[OUT]:卡片序列号，4字节
 * @return char 返回状态值，表示操作是否成功：
 *         - MI_OK: 防碰撞成功
 *         - MI_ERR: 防碰撞失败，可能的原因包括卡响应错误等
 */
char PcdAnticoll(unsigned char *pSnr)
{
    char status;
    unsigned char i, snr_check = 0;
    unsigned int unLen;
    unsigned char ucComMF522Buf[MAXRLEN];

    // 清除Status2寄存器的位0x08，准备防碰撞
    ClearBitMask(Status2Reg, 0x08);
    // 设置位帧处理寄存器为0x00，确保通信不受其他设置影响
    WriteRawRC(BitFramingReg, 0x00);
    // 清除碰撞寄存器的位0x80，初始化防碰撞过程
    ClearBitMask(CollReg, 0x80);

    // 准备发送的数据：抗碰撞命令+0x20
    ucComMF522Buf[0] = PICC_ANTICOLL1;
    ucComMF522Buf[1] = 0x20;

    // 与PICC进行通信，执行防碰撞操作
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &unLen);

    if (status == MI_OK)
    {
        // 接收响应，将得到的SNR存入pSnr，并计算校验和
        for (i = 0; i < 4; i++)
        {
            *(pSnr + i) = ucComMF522Buf[i];
            snr_check ^= ucComMF522Buf[i];
        }
        // 检查校验和是否匹配，如果不匹配则设置状态为错误
        if (snr_check != ucComMF522Buf[i])
        {
            status = MI_ERR;
        }
    }

    // 防碰撞过程结束，重新设置碰撞寄存器的位0x80
    SetBitMask(CollReg, 0x80);
    return status;
}

/**
 * @brief PC/SC通信模块复位函数  复位RC522
 *
 * 本函数实现了对MF522芯片的复位操作，通过控制复位引脚（MF522_RST）进行芯片硬件复位，
 * 并配置相关寄存器以初始化通信模块，使其进入准备接收命令的状态。
 *
 * @return 返回MI_OK，表示操作成功。
 */
char PcdReset(void)
{
    // 芯片去复位
    MF522_RST = 1;
    // 延时，保证状态稳定
    _nop_();
    // 芯片复位
    MF522_RST = 0;
    // 延时，保证状态稳定
    _nop_();
    // 再次去复位
    MF522_RST = 1;
    // 延时，保证状态稳定
    _nop_();
    // 初始化通信阶段，向命令寄存器写入复位命令
    WriteRawRC(CommandReg, PCD_RESETPHASE);
    // 延时，保证写入操作完成
    _nop_();

    // 设置模式寄存器，用于配置CRC初始值和预设值
    WriteRawRC(ModeReg, 0x3D);
    // 设置定时器重装寄存器低8位，用于配置通信定时
    WriteRawRC(TReloadRegL, 30);
    // 设置定时器重装寄存器高8位，完成定时器重装值配置
    WriteRawRC(TReloadRegH, 0);
    // 配置定时器模式寄存器，设置内部定时器工作模式
    WriteRawRC(TModeReg, 0x8D);
    // 设置定时器预分频寄存器，用于进一步调整定时器频率
    WriteRawRC(TPrescalerReg, 0x3E);
    // 配置自动传输寄存器，用于自动传输数据
    WriteRawRC(TxAutoReg, 0x40);
    // 返回成功状态
    return MI_OK;
}

/**
 * 设置RC632的工作方式
 * 配置PCD（Proximity Coupling Device）的ISO类型
 *
 * 此函数用于配置PCD以与符合ISO14443_A标准的卡片进行通信
 * 它通过设置寄存器的值来调整PCD的工作模式，以适应ISO14443_A卡片的要求
 *
 * 参数:
 * type - 卡片的ISO类型，目前只支持'A'表示ISO14443_A
 *
 * 返回值:
 * 如果类型匹配，则返回MI_OK表示配置成功
 * 否则返回-1，表示不支持的类型
 */
char M500PcdConfigISOType(unsigned char type)
{
    // 检查类型是否为ISO14443_A
    if (type == 'A') // ISO14443_A
    {
        // 配置Status2Reg，清除特定的位掩码
        ClearBitMask(Status2Reg, 0x08);
        // 配置ModeReg，设置为特定的值
        WriteRawRC(ModeReg, 0x3D); // 3
        // 配置RxSelReg，设置为特定的值
        WriteRawRC(RxSelReg, 0x86); //
        // 配置RFCfgReg，设置为特定的值
        WriteRawRC(RFCfgReg, 0x7F); // 4F
        // 配置TReloadRegL和TReloadRegH，设置超时长度
        WriteRawRC(TReloadRegL, 30); // tmoLength);// TReloadVal = 'h6a =tmoLength(dec)
        WriteRawRC(TReloadRegH, 0);
        // 配置TModeReg，设置为特定的值
        WriteRawRC(TModeReg, 0x8D);
        // 配置TPrescalerReg，设置为特定的值
        WriteRawRC(TPrescalerReg, 0x3E);
        // 延迟一段时间，以确保设置生效
        delay_n10ms(1);
        // 打开天线，开始RF场
        PcdAntennaOn();
    }
    else { return -1; } // 如果类型不匹配，返回错误码-1

    return MI_OK; // 返回成功码表示配置成功
}

/**
 * 读取原始的RC值  读RC632寄存器
 *
 * 本函数通过SPI通信方式，从指定的地址读取一个字节的数据
 * 主要用于与RC522系列RFID模块进行数据交换
 *
 * @param Address 要读取的地址，经过左移和或运算处理后，用于SPI通信的数据发送  Address[IN]:寄存器地址
 * @return 返回从指定地址读取到的数据
 */
unsigned char ReadRawRC(unsigned char Address)
{
    unsigned char i, ucAddr;
    unsigned char ucResult = 0;

    // 时钟线置低，开始SPI通信
    MF522_SCK = 0;
    // 设备选择线置低，选中SPI设备
    MF522_NSS = 0;
    // 对地址进行处理，用于SPI数据发送
    ucAddr = ((Address << 1) & 0x7E) | 0x80;

    // 发送处理后的地址
    for (i = 8; i > 0; i--)
    {
        // 根据地址的最高位发送数据
        MF522_SI = ((ucAddr & 0x80) == 0x80);
        // 拉高时钟线，准备发送数据
        MF522_SCK = 1;
        // 地址左移一位，准备发送下一位
        ucAddr <<= 1;
        // 拉低时钟线，完成数据发送
        MF522_SCK = 0;
    }

    // 接收从设备返回的数据
    for (i = 8; i > 0; i--)
    {
        // 拉高时钟线，准备接收数据
        MF522_SCK = 1;
        // 接收到的数据左移一位，并将新位加入
        ucResult <<= 1;
        // 加入接收到的位
        ucResult |= (bit)MF522_SO;
        // 拉低时钟线，完成数据接收
        MF522_SCK = 0;
    }

    // 取消选中SPI设备
    MF522_NSS = 1;
    // 拉高时钟线，结束SPI通信
    MF522_SCK = 1;
    // 返回接收到的数据
    return ucResult;
}

/**
 * 向RC522 MF522_SDA总线写入一个字节的数据  写RC632寄存器
 *
 * @param Address 要写入的寄存器地址
 * @param value 要写入的值
 *
 * 该函数通过SPI协议向RC522模块的指定寄存器写入一个字节的数据
 * 它首先将NSS（片选信号）拉低，然后通过SI（数据输入）和SCK（时钟）引脚
 * 依次发送寄存器地址和值的每一位，最后将NSS拉高结束通信
 */
void WriteRawRC(unsigned char Address, unsigned char value)
{
    unsigned char i, ucAddr;

    // 将时钟SCK设置为低电平
    MF522_SCK = 0;
    // 将片选信号NSS拉低，启动SPI通信
    MF522_NSS = 0;
    // 对地址进行处理，左移一位并保留低9位
    ucAddr = ((Address << 1) & 0x7E);

    // 发送寄存器地址的每一位
    for (i = 8; i > 0; i--)
    {
        // 设置SI引脚的电平，根据当前地址位是0还是1
        MF522_SI = ((ucAddr & 0x80) == 0x80);
        // 拉高SCK时钟，触发数据传输
        MF522_SCK = 1;
        // 地址左移一位，为发送下一位做准备
        ucAddr <<= 1;
        // 时钟SCK重新拉低，完成一位数据的发送
        MF522_SCK = 0;
    }

    // 发送数据值的每一位
    for (i = 8; i > 0; i--)
    {
        // 设置SI引脚的电平，根据当前值位是0还是1
        MF522_SI = ((value & 0x80) == 0x80);
        // 拉高SCK时钟，触发数据传输
        MF522_SCK = 1;
        // 值左移一位，为发送下一位做准备
        value <<= 1;
        // 时钟SCK重新拉低，完成一位数据的发送
        MF522_SCK = 0;
    }

    // 结束SPI通信，将片选信号NSS拉高
    MF522_NSS = 1;
    // 将时钟SCK设置为高电平，确保通信干净利落地结束
    MF522_SCK = 1;
}

/**
 * SetBitMask 函数的作用是设置指定寄存器的某些位为1。 置RC522寄存器位
 *
 * @param reg 寄存器的地址，表明需要操作哪个寄存器。reg[IN]:寄存器地址
 * @param mask 需要设置为1的位的掩码。掩码中为1的位置表示对应寄存器的位将被设置为1。  mask[IN]:置位值
 *
 * 该函数首先读取指定寄存器的当前值，然后使用或运算（|）将掩码应用到寄存器的值上，
 * 从而确保掩码中为1的位被设置为1，最后将修改后的值写回寄存器。
 */
void SetBitMask(unsigned char reg, unsigned char mask)
{
    // 临时变量，用于存储寄存器的原始值
    char tmp = 0x0;

    // 读取指定寄存器的当前值
    tmp = ReadRawRC(reg);

    // 将掩码应用到寄存器的值上，并将结果写回寄存器
    WriteRawRC(reg, tmp | mask); // 设置位掩码
}

/**
 * @brief 清除寄存器中指定的位掩码  清RC522寄存器位
 *
 * 该函数通过将mask掩码应用到特定的寄存器上，来清除（置位为0）寄存器中对应的位。
 * 这在硬件控制中非常有用，比如关闭某个中断或者改变某个设置的状态。
 *
 * @param reg 目标寄存器的地址   reg[IN]:寄存器地址
 * @param mask 要清除的位的掩码   mask[IN]:清位值
 */
void ClearBitMask(unsigned char reg, unsigned char mask)
{
    // 定义一个临时变量来存储寄存器的原始值
    char tmp = 0x0;

    // 从指定寄存器读取原始值
    tmp = ReadRawRC(reg);

    // 将原始值与掩码进行与非操作，然后写回寄存器，效果是清除掩码对应的位
    WriteRawRC(reg, tmp & ~mask); // clear bit mask
}

//
// 开启天线
// 每次启动或关闭天险发射之间应至少有1ms的间隔
//
void PcdAntennaOn()
{
    unsigned char i;
    i = ReadRawRC(TxControlReg);
    if (!(i & 0x03))
    {
        SetBitMask(TxControlReg, 0x03);
    }
}


/**
 * PcdAntennaOff 函数用于关闭天线。
 * 
 * 该函数通过清除TxControlReg寄存器中的特定比特位来实现天线的关闭。
 * 通过将TxControlReg寄存器的低两位（比特0和比特1）的值设置为0，从而达到关闭天线的目的。
 */
void PcdAntennaOff()
{
    ClearBitMask(TxControlReg, 0x03);
}


/** 
 * @brief MF522命令通讯处理函数   通过RC522和ISO14443卡通讯
 * 
 * 本函数根据指定的命令与MF522读写器进行通讯，准备输入数据，发送命令，
 * 并根据命令类型接收和处理响应数据。
 * 
 * @param Command 命令类型，如PCD_AUTHENT认证、PCD_TRANSCEIVE传输等。  Command[IN]:RC522命令字
 * @param pInData 指向发送数据的指针。  pIn [IN]:通过RC522发送到卡片的数据
 * @param InLenByte 发送数据的字节长度。   InLenByte[IN]:发送数据的字节长度
 * @param pOutData 指向接收响应数据的指针。    pOut [OUT]:接收到的卡片返回数据
 * @param pOutLenBit 指向接收数据的位长度的指针。   *pOutLenBit[OUT]:返回数据的位长度
 * @return char 返回通讯状态，MI_OK表示成功，其他值如MI_ERR表示错误。
 */
char PcdComMF522(unsigned char Command, 
                 unsigned char *pInData, 
                 unsigned char InLenByte,
                 unsigned char *pOutData, 
                 unsigned int  *pOutLenBit)
{
    char status = MI_ERR; // 初始化状态为错误
    unsigned char irqEn = 0x00; // 中断使能
    unsigned char waitFor = 0x00; // 等待的条件
    unsigned char lastBits;
    unsigned char n;
    unsigned int i;
    
    // 根据命令类型设置中断使能和等待条件
    switch (Command)
    {
       case PCD_AUTHENT:
          irqEn = 0x12;
          waitFor = 0x10;
          break;
       case PCD_TRANSCEIVE:
          irqEn = 0x77;
          waitFor = 0x30;
          break;
       default:
         break;
    }
   
    // 配置寄存器，准备发送命令
    WriteRawRC(ComIEnReg, irqEn | 0x80);
    ClearBitMask(ComIrqReg, 0x80);
    WriteRawRC(CommandReg, PCD_IDLE);
    SetBitMask(FIFOLevelReg, 0x80);
    
    // 写入发送数据到FIFO
    for (i = 0; i < InLenByte; i++)
    {   
        WriteRawRC(FIFODataReg, pInData[i]);
    }
    // 发送命令
    WriteRawRC(CommandReg, Command);
    
    // 特定命令下的额外配置
    if (Command == PCD_TRANSCEIVE)
    {   
        SetBitMask(BitFramingReg, 0x80);
    }
    
    // 等待命令执行完成，超时控制
    i = 800; // 控制等待时间的计数器
    do 
    {
         n = ReadRawRC(ComIrqReg);
         i--;
    }
    while ((i != 0) && !(n & 0x01) && !(n & waitFor));
    ClearBitMask(BitFramingReg, 0x80);
	      
    // 如果在超时前收到响应
    if (i != 0)
    {    
         if (!(ReadRawRC(ErrorReg) & 0x1B))
         {
             status = MI_OK; // 设置状态为成功
             if (n & irqEn & 0x01)
             {   
                 status = MI_NOTAGERR; // 处理没有标签的情况
             }
             if (Command == PCD_TRANSCEIVE)
             {
                n = ReadRawRC(FIFOLevelReg);
                lastBits = ReadRawRC(ControlReg) & 0x07;
                // 计算接收到的数据位数
                if (lastBits)
                {   
                    *pOutLenBit = (n-1)*8 + lastBits;
                }
                else
                {   
                    *pOutLenBit = n*8;
                }
                if (n == 0)
                {   
                    n = 1;
                }
                if (n > MAXRLEN)
                {   
                    n = MAXRLEN;
                }
                // 读取响应数据
                for (i = 0; i < n; i++)
                {   
                    pOutData[i] = ReadRawRC(FIFODataReg);
                }
            }
         }
         else
         {   
             status = MI_ERR; // 设置状态为错误
         }
    }
    // 命令执行结束，重置定时器和命令状态
    SetBitMask(ControlReg, 0x80);
    WriteRawRC(CommandReg, PCD_IDLE); 
    return status; // 返回通讯状态
}



/**
 * 函数名：PcdSelect  选定卡片
 * 功能：根据给定的SN号（序列号）选择一个特定的PICC（ proximity integrated circuit card，近场通信集成电路卡）
 * 参数：pSnr[IN]:卡片序列号，4字节
 *      *pSnr: 指向SN号的指针，SN号是一个字节数组  
 * 返回值：
 *      返回选择操作的状态，MI_OK表示成功，MI_ERR表示失败
 *
 * 说明：此函数构建一个选择命令，其中包括SN号，并将其发送到PICC。
 *       如果PICC响应正确，返回MI_OK，否则返回MI_ERR。
 */

char PcdSelect(unsigned char *pSnr)
{
    char status;  // 定义状态变量
    unsigned char i;  // 用于循环的索引变量
    unsigned int unLen;  // 定义接收数据的长度变量
    unsigned char ucComMF522Buf[MAXRLEN];  // 定义命令缓冲区
    
    // 初始化命令缓冲区
    ucComMF522Buf[0] = PICC_ANTICOLL1;  // 命令的第一个字节是抗冲突的标志
    ucComMF522Buf[1] = 0x70;  // 命令的第二个字节，特定的功能选择
    ucComMF522Buf[6] = 0;  // 初始化校验和
    
    // 复制SN号到命令缓冲区，并更新校验和
    for (i = 0; i < 4; i++)
    {
        ucComMF522Buf[i + 2] = *(pSnr + i);  // 将SN号的字节复制到命令缓冲区
        ucComMF522Buf[6] ^= *(pSnr + i);  // 更新校验和
    }
    
    // 计算CRC（循环冗余校验）
    CalulateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]);
  
    // 清除状态寄存器中的特定位，准备发送命令
    ClearBitMask(Status2Reg, 0x08);

    // 发送选择命令到PICC
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen);
    
    // 检查操作状态和返回数据的长度
    if ((status == MI_OK) && (unLen == 0x18))
    {  
        status = MI_OK;  // 如果操作成功且数据长度正确，状态为OK
    }
    else
    {  
        status = MI_ERR;  // 否则，状态为错误
    }

    return status;  // 返回操作状态
}


/**
 * @brief 执行PCD（Proximity Coupling Device）认证流程   验证卡片密码
 * 
 * 本函数准备必要的参数并调用底层函数执行PCD认证流程。认证过程需要提供认证模式、
 * 地址、密钥和序列号。函数内部组装这些参数并调用PcdComMF522函数进行认证。认证成功
 * 或失败的状态将被返回。
 * 
 * @param auth_mode 认证模式，通常是由特定的认证命令或算法决定的一个字节。  auth_mode[IN]: 密码验证模式 0x60 = 验证A密钥  0x61 = 验证B密钥 
 * @param addr 认证的设备地址，用于区分不同的设备或扇区。  addr[IN]：块地址
 * @param pKey 指向认证密钥的指针，通常是一个6字节的数组。  pKey[IN]：密码
 * @param pSnr 指向设备序列号的指针，通常是一个6字节的数组。   pSnr[IN]：卡片序列号，4字节
 * @return char 返回认证状态，MI_OK表示认证成功，MI_ERR表示认证失败。
 */
char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr)
{
    char status; // 用于存储认证状态
    unsigned int  unLen; // 用于存储响应长度
    unsigned char i,ucComMF522Buf[MAXRLEN]; // 缓冲区用于组装认证命令和参数

    // 组装认证命令和参数
    ucComMF522Buf[0] = auth_mode; // 认证模式
    ucComMF522Buf[1] = addr; // 设备地址
    // 复制认证密钥到缓冲区
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+2] = *(pKey+i);   }
    // 复制设备序列号到缓冲区
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+8] = *(pSnr+i);   }
    
    // 调用底层函数执行认证
    status = PcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);
    // 检查认证结果
    if ((status != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08)))
    {   status = MI_ERR;   }
    
    return status; // 返回认证状态
}


/**
 * 从PICC读取块数据   读取M1卡一块数据
 * 
 * @param addr 要读取的块地址   addr[IN]：块地址
 * @param pData 接收读取数据的缓冲区  读出的数据，16字节
 * @return 返回读取状态，MI_OK表示成功，其他值表示错误
 * 
 * 该函数通过MF522芯片与PICC通信，读取指定地址的数据块，包括计算CRC校验、发送读取命令、接收并校验响应数据
 */
char PcdRead(unsigned char addr, unsigned char *pData)
{
    // 通信状态
    char status;
    // 接收数据长度
    unsigned int unLen;
    // 通信命令缓冲区
    unsigned char i, ucComMF522Buf[MAXRLEN]; 

    // 准备读取命令，第一个字节是命令码，第二个字节是读取的块地址
    ucComMF522Buf[0] = PICC_READ;
    ucComMF522Buf[1] = addr;
    // 计算并附加CRC校验码
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
   
    // 向MF522发送读取命令，并接收响应
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
    // 如果操作成功且响应长度正确
    if ((status == MI_OK) && (unLen == 0x90))
    {
        // 将接收到的数据复制到 pData 指向的缓冲区
        for (i = 0; i < 16; i++)
        {
            *(pData + i) = ucComMF522Buf[i];
        }
    }
    else
    {
        // 操作失败或响应长度错误，设置状态为错误
        status = MI_ERR;
    }
    
    // 返回操作状态
    return status;
}


/**
 * @brief 对PICC进行写操作  写数据到M1卡一块
 * 
 * 本函数负责向指定地址写入数据。首先构建写命令的数据包，然后计算CRC校验，通过PcdComMF522函数发送命令，
 * 并接收响应。如果响应正常，再将具体数据与命令一起发送，完成写操作。
 * 
 * @param addr 要写入的块地址  addr[IN]：块地址
 * @param pData 指向要写入的数据的指针   写入的数据，16字节
 * @return char 返回写操作的状态，MI_OK表示成功，MI_ERR表示错误
 */
char PcdWrite(unsigned char addr,unsigned char *pData)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 

    // 构建写命令的数据包
    ucComMF522Buf[0] = PICC_WRITE;
    ucComMF522Buf[1] = addr;
    // 计算CRC校验并存入数据包
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    // 发送写命令，接收响应
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    // 检查响应是否正确
    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
        
    // 如果命令响应正确，则准备写入数据
    if (status == MI_OK)
    {
        // 将数据复制到临时缓冲区
        for (i=0; i<16; i++)
        {    ucComMF522Buf[i] = *(pData+i);   }
        // 计算数据块的CRC校验
        CalulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);

        // 发送数据块，完成写操作
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);
        // 再次检查响应是否正确
        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {   status = MI_ERR;   }
    }
    
    // 返回写操作的状态
    return status;
}


/////////////////////////////////////////////////////////////////////
//用MF522计算CRC16函数
/////////////////////////////////////////////////////////////////////
void CalulateCRC(unsigned char *pIn ,unsigned char   len,unsigned char *pOut )
{
    unsigned char i,n;
    ClearBitMask(DivIrqReg,0x04);
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    for (i=0; i<len; i++)
    {   WriteRawRC(FIFODataReg, *(pIn +i));   }
    WriteRawRC(CommandReg, PCD_CALCCRC);
    i = 0xFF;
    do 
    {
        n = ReadRawRC(DivIrqReg);
        i--;
    }
    while ((i!=0) && !(n&0x04));
    pOut [0] = ReadRawRC(CRCResultRegL);
    pOut [1] = ReadRawRC(CRCResultRegM);
}
//
    // 功    能：扣款和充值
    // 参数说明: dd_mode[IN]：命令字
    //                0xC0 = 扣款
    //                0xC1 = 充值
    //           addr[IN]：钱包地址
    //           pValue[IN]：4字节增(减)值，低位在前
    // 返    回: 成功返回MI_OK
    //
// char PcdValue(unsigned char dd_mode,unsigned char addr,unsigned char *pValue)
//{
//     char status;
//     unsigned long  unLen;
//     unsigned char i,ucComMF522Buf[MAXRLEN];
//
//     ucComMF522Buf[0] = dd_mode;
//     ucComMF522Buf[1] = addr;
//     CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
//
//     status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
//
//     if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
//     {   status = MI_ERR;   }
//
//     if (status == MI_OK)
//     {
//        // memcpy(ucComMF522Buf, pValue, 4);
//         for (i=0; i<16; i++)
//         {    ucComMF522Buf[i] = *(pValue+i);   }
//         CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);
//         unLen = 0;
//         status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
//         if (status != MI_ERR)
//         {    status = MI_OK;    }
//     }
//
//     if (status == MI_OK)
//     {
//         ucComMF522Buf[0] = PICC_TRANSFER;
//         ucComMF522Buf[1] = addr;
//         CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
//
//         status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
//
//         if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
//         {   status = MI_ERR;   }
//     }
//     return status;
// }
/*
/
//功    能：备份钱包
//参数说明: sourceaddr[IN]：源地址
//          goaladdr[IN]：目标地址
//返    回: 成功返回MI_OK
/
char PcdBakValue(unsigned char sourceaddr, unsigned char goaladdr)
{
    char status;
    unsigned long  unLen;
    unsigned char ucComMF522Buf[MAXRLEN];

    ucComMF522Buf[0] = PICC_RESTORE;
    ucComMF522Buf[1] = sourceaddr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }

    if (status == MI_OK)
    {
        ucComMF522Buf[0] = 0;
        ucComMF522Buf[1] = 0;
        ucComMF522Buf[2] = 0;
        ucComMF522Buf[3] = 0;
        CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);

        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
        if (status != MI_ERR)
        {    status = MI_OK;    }
    }

    if (status != MI_OK)
    {    return MI_ERR;   }

    ucComMF522Buf[0] = PICC_TRANSFER;
    ucComMF522Buf[1] = goaladdr;

    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }

    return status;
}
*/
